package DataFile;

import java.io.File;
import java.io.FileOutputStream;
import java.io.FileWriter;
import java.io.IOException;
import java.nio.ByteBuffer;

//import ClassData.ClassData;
import ClassData.ClassDataList;
//import ClassData.ConstantPoolClass_Info;
//import ClassData.ConstantPoolUtf8_Info;
//import ClassData.FieldInfo;
//import ClassData.MethodInfo;
import NativeInterface.NativeFunction;
import NativeInterface.NativeInterface;

public class DataFileCreator {

	public static final int MEMORY_SIZE        = 64000;
	public static final int BASE_MEMORY_ADRESS = 0x80030000;	// JavaCube UC3A1512
	
	// attributes
	ByteBuffer dataBufer;

	private int BaseAdress;
	private int MemorySize;

	private NativeInterface myNativeInterface;

public DataFileCreator(ClassDataList classList,String rootPath,String projectName)
{
 
	File binaryOutputFile;
	
	int classCounter;
	int MemoryAdress;
	
	Structure_ClassData tmpClassData;
	Structure_ClassTable myClassTable;

	
	myNativeInterface= new NativeInterface();
	
	BaseAdress=BASE_MEMORY_ADRESS;
	
	dataBufer=ByteBuffer.allocate(MEMORY_SIZE);	
	/*
	classTable = new int [classList.size()+1];
	classTable[0]=classList.size();
	
	for (classCounter=0;classCounter<classList.size();classCounter++)
		{
		writeClass(classList.get(classCounter));
		
		}
	
	writeClassTable();
	*/
	


	/******************* Create memory structures  ****************************/

	// 1. Fill Structure_ClassTable with new Structure_ClassData
	// 2. Sort Structure_ClassTable
	
	myClassTable = new Structure_ClassTable();
	
	// add classes to class table
	for (classCounter=0;classCounter < classList.size();classCounter++) {
		tmpClassData= new Structure_ClassData(classList.get(classCounter));
		myClassTable.add(tmpClassData);
	}
	
	sortClasses(myClassTable);
	
	MemoryAdress=BaseAdress;

	/******************* Calculate Memory Map ****************************/

	MemoryAdress = myClassTable.setBaseAddress(MemoryAdress);

	for (classCounter=0;classCounter<classList.size();classCounter++) {
		tmpClassData=myClassTable.get(classCounter);
		MemoryAdress=tmpClassData.setBaseAdress(MemoryAdress);
	}

	MemorySize=MemoryAdress-BASE_MEMORY_ADRESS;
	/******************* Link References ****************************/

// insert Classes for Arrays !!!
	
	makeClassReferences(myClassTable);	
	makeFieldReferences(myClassTable);
	makeMethodReferences(myClassTable);
	
	for (classCounter=0;classCounter<classList.size();classCounter++) {
		tmpClassData=myClassTable.get(classCounter);
		tmpClassData.setStaticDataHandle((short)(classCounter+1));		
	}

	/******************* Output Memory Map ****************************/
	
	File memoryMapFile;
	
	memoryMapFile = new File(rootPath+"/MemoryMap.map");
	
	try {
		memoryMapFile.createNewFile();
		FileWriter memoryMapWritter = new FileWriter(memoryMapFile);

		memoryMapWritter.write(myClassTable.getMemoryMapAsString());
		memoryMapWritter.flush();
	
		for (classCounter=0;classCounter<classList.size();classCounter++) {
			tmpClassData=myClassTable.get(classCounter);
			System.out.println(tmpClassData.getClassName());
			memoryMapWritter.write(tmpClassData.getMemoryMapAsString());
			memoryMapWritter.flush();
		}
	
		memoryMapWritter.close();
	
	} catch (IOException e) {
		System.out.println("Errorr !!!!!"+e.getMessage());
	}
	
	/******************* Output Linker Map ****************************/

	
	// Link references
	
	System.out.println("size :"+(MemoryAdress-BaseAdress));
	
	
	/****************************** make binary file **************************************/	
	
	ByteBuffer fileData; 
	fileData = ByteBuffer.allocate(MemorySize);
		
	myClassTable.writeData(fileData);		
		
	for (classCounter=0;classCounter<classList.size();classCounter++) {
		tmpClassData=myClassTable.get(classCounter);
		tmpClassData.writeData(fileData);
		//MemoryAdress=tmpClassData.setBaseAdress(MemoryAdress);
		//System.out.println(MemoryAdress);
	}

	try {
		
		binaryOutputFile=new File(rootPath+"/"+projectName+".bin");
		binaryOutputFile.createNewFile();
		FileOutputStream binaryOutput  = new FileOutputStream (binaryOutputFile);


		int counter;
		
		System.out.println("MemorySize "+MemorySize);
		
		fileData.rewind();
		
		for (counter=0;counter<MemorySize;counter++) {
			binaryOutput.write(fileData.get());
		}
		
		binaryOutput.close();
	} catch (IOException e) {
		System.out.println("Errorr !!!!!"+e.getMessage());
	}
}

private void sortClasses(Structure_ClassTable myClassTable) {

	movieClass(myClassTable,"java/lang/Object",0);
	movieClass(myClassTable,"java/lang/String",1);
	
	// exceptions
	movieClass(myClassTable,"java/lang/Throwable",2);
	movieClass(myClassTable,"java/lang/Exception",3);
	movieClass(myClassTable,"java/lang/NullPointerException",4);
	movieClass(myClassTable,"java/lang/ArithmeticException",5);
	
}

private void movieClass(Structure_ClassTable myClassTable,String className,int position) {
	Structure_ClassData actualClass;	
	int classCounter;

	for (classCounter=0;classCounter<myClassTable.size();classCounter++) {
		actualClass=myClassTable.get(classCounter);
		
		actualClass.getClassName();
		if (actualClass.getClassName().equals(className)) {
			myClassTable.move(actualClass, position);
		}
	}
}

private void makeClassReferences(Structure_ClassTable myClassTable) {
	Structure_ClassData actualClass;	
	Structure_ClassData destinationClass;
	
	Structure_CP_Table constantPoolTable;
	int classCounter;
	int constantCounter;
	int classIndex;
	Structure_CP_ClassInfo myClassInfo;
	String className;

	Structure_CP_UTF8Info myUtf8Info;
	
	for (classCounter=0;classCounter<myClassTable.size();classCounter++) {
		actualClass=myClassTable.get(classCounter);
		constantPoolTable=actualClass.getConstantPoolTable();
		
		for (constantCounter=1;constantCounter<constantPoolTable.size()+1;constantCounter++) {
			if (constantPoolTable.get(constantCounter).getClass().getName().equals(Structure_CP_ClassInfo.class.getName())) {
				myClassInfo=(Structure_CP_ClassInfo)constantPoolTable.get(constantCounter);
			
				classIndex=myClassInfo.getClassNameIndex();
				System.out.println(actualClass.getClassName()+" -> Class Index "+classIndex);
				
				if (actualClass.getClassName().startsWith("[")) {
					int a;
					a=5;
				}
				
				if (constantPoolTable.get(classIndex).getClass().getName().equals(Structure_CP_UTF8Info.class.getName())) {
					myUtf8Info=(Structure_CP_UTF8Info)constantPoolTable.get(classIndex);
					className=myUtf8Info.getString();
//				className=constantPoolTable.getClassName(classIndex);
					destinationClass=myClassTable.getClass(className);
				
					if (destinationClass!=null) {
						myClassInfo.setClassAdress(destinationClass.getBaseAdress());
					} else  {
						//is it an array ??
						// no search in superclass
						System.out.println("missing class - "+className);
					}
				}
			}
		}
	}
}


private void makeFieldReferences(Structure_ClassTable myClassTable) {
	Structure_ClassData actualClass;	
	Structure_ClassData fieldClass;

	Structure_CP_Table constantPoolTable;
	int classCounter;
	int constantCounter;
	int classIndex;
	Structure_CP_FieldInfo myFieldInfo;
	String className;
	String fieldName;
	Structure_FieldData	destinationField;
	
	
	for (classCounter=0;classCounter<myClassTable.size();classCounter++) {
		actualClass=myClassTable.get(classCounter);
		constantPoolTable=actualClass.getConstantPoolTable();
		
		for (constantCounter=1;constantCounter<constantPoolTable.size()+1;constantCounter++) {
			if (constantPoolTable.get(constantCounter).getClass().getName().equals(Structure_CP_FieldInfo.class.getName())) {
				myFieldInfo=(Structure_CP_FieldInfo)constantPoolTable.get(constantCounter);
				fieldName=actualClass.getFieldName(myFieldInfo);
				
				classIndex=myFieldInfo.getFieldClassIndex();
				
				className=constantPoolTable.getClassName(classIndex);

				destinationField=null;
				do {
					fieldClass=myClassTable.getClass(className);
					System.out.println("Searching for field : "+fieldName+"  in : "+className);

					if (fieldClass!=null) {
						destinationField=fieldClass.getField(fieldName);
						
						if (destinationField==null) {
							className=fieldClass.getSuperClassName(); 
						} else {
							myFieldInfo.setClassAddress(fieldClass.getBaseAddress());
								myFieldInfo.setFieldAddress(destinationField.getBaseAddress());
						}
					}
				} while ((destinationField==null) && (fieldClass!=null)) ;

				System.out.println();
			} // if
		} // for
	} // for
}

private void makeMethodReferences(Structure_ClassTable myClassTable) {
	Structure_ClassData actualClass;	
	Structure_ClassData methodClass;

	Structure_CP_Table constantPoolTable;
	int classCounter;
	int constantCounter;
	int classIndex;
	
	Structure_CP_MethodInfo myMethodInfo;
	String className;
	String methodName;
	Structure_MethodData	destinationMethod;
	
	for (classCounter=0;classCounter<myClassTable.size();classCounter++) {
		actualClass=myClassTable.get(classCounter);
		constantPoolTable=actualClass.getConstantPoolTable();
		
		for (constantCounter=1;constantCounter<constantPoolTable.size()+1;constantCounter++) {
			if (constantPoolTable.get(constantCounter).getClass().getName().equals(Structure_CP_MethodInfo.class.getName())) {
				myMethodInfo=(Structure_CP_MethodInfo)constantPoolTable.get(constantCounter);

//				myNativeInterface
				methodName=actualClass.getMethodName(myMethodInfo);
				
				classIndex=myMethodInfo.getClassIndex();
				
				className=constantPoolTable.getClassName(classIndex);

				if (methodName.equals("descent"))
				{
					int a;
					a=5;
					
				}
				
				//TODO if native find Native function;
				destinationMethod=null;
				do {
					System.out.println("Searching for method : "+methodName+" "+myMethodInfo.getParametersAsString()+" in : "+className);
					methodClass=myClassTable.getClass(className);

					if (methodClass!=null) {
						destinationMethod=methodClass.getMethod(methodName,myMethodInfo.getParametersAsString());
						
						if (destinationMethod==null) {
							className=methodClass.getSuperClassName(); 
						} else {
							myMethodInfo.setAdress(destinationMethod.getBaseAddress());
							myMethodInfo.setRealClassAdress(methodClass.getBaseAddress());
						}
					}
				} while ((destinationMethod==null) && (methodClass!=null)) ;
				
				if(destinationMethod!=null)
					if (destinationMethod.isNative()) {
						NativeFunction myNativeFunction;						
						
						myNativeFunction=myNativeInterface.findFunction(destinationMethod.getMethodName(), methodClass.getClassName(), destinationMethod.getParametersAsString());
						if (myNativeFunction!=null) {
							destinationMethod.setFunctionNumber(myNativeFunction.getFunctionNumber());
						}
						
						//TODO if not Found EXCEPTION
						System.out.println(destinationMethod.getMethodName()+"  "+ methodClass.getClassName()+"  "+destinationMethod.getParametersAsString()+"  "+
								myNativeInterface.findFunction(destinationMethod.getMethodName(), methodClass.getClassName(), destinationMethod.getParametersAsString()));
					}
				System.out.println();	
				
/*				else {
					System.out.println("class not found - "+className);
				}
*/

//				fieldClass.getField(fieldName)
				// find class
				// find Field in class
				// get field Adress
				}
			} // if
		} // for
	} // for
}







/*
private void createArrayClasses(Structure_ClassTable myClassTable)
{
	Structure_ClassData actualClass;	
	Structure_ClassData destinationClass;
	
	Structure_CP_Table constantPoolTable;
	int classCounter;
	int constantCounter;
	int classIndex;
	Structure_CP_ClassInfo myClassInfo;
	String className;

	Structure_CP_UTF8Info myUtf8Info;
	
	
	for (classCounter=0;classCounter<myClassTable.size();classCounter++)
		{
		actualClass=myClassTable.get(classCounter);
		constantPoolTable=actualClass.getConstantPoolTable();
		
		for (constantCounter=1;constantCounter<constantPoolTable.size()+1;constantCounter++)
			{
			if (constantPoolTable.get(constantCounter).getClass().getName().equals(Structure_CP_ClassInfo.class.getName()))
				{
				myClassInfo=(Structure_CP_ClassInfo)constantPoolTable.get(constantCounter);
			
				classIndex=myClassInfo.getClassNameIndex();
				
				if 	 (constantPoolTable.get(classIndex).getClass().getName().equals(Structure_CP_UTF8Info.class.getName()))
					{
					myUtf8Info=(Structure_CP_UTF8Info)constantPoolTable.get(classIndex);
					className=myUtf8Info.getString();
					if (className.startsWith("["))
						{
						System.out.println("Serching array "+ className);
						destinationClass=myClassTable.getClass(className);
						createArrayClass(className);
						}
//				className=constantPoolTable.getClassName(classIndex);
				
					 
					}
				}
			}
		
		
		}
		
}


private Structure_CP_ClassInfo createArrayClass(String className)
{
	Structure_CP_ClassInfo myClass;
	
	ConstantPoolClass_Info myClassInfo;
	ConstantPoolUtf8_Info myClassName;
	
	
	myClassName = new ConstantPoolUtf8_Info(className);
	
	myClassInfo=new ConstantPoolClass_Info();
	
	myClass=new Structure_CP_ClassInfo();
	
	return(myClass)
}
*/

